/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2018. All rights reserved.
 * Description: Huawei safe print function define
 *
 * Huawei(R) HiNIC PCI Express Network Controller UEFI Driver.
 * This driver is only usable in the EFI Pre-boot execution environment.
 * All rights reserved.
 * Author	 :
 * Create	 : 2018/12/10
 */
#include "HwSafeOpInternal.h"
#include "HwSafeString.h"

static BOOLEAN IsBufOverlap(
    const VOID    *Base1,
    const UINTN   Size1,
    const VOID    *Base2,
    const UINTN   Size2)
{
    if ((((UINT8 *)Base1 >= (UINT8 *)Base2) && ((UINT8 *)Base1 < (UINT8 *)Base2 + Size2)) ||
        (((UINT8 *)Base2 >= (UINT8 *)Base1) && ((UINT8 *)Base2 < (UINT8 *)Base1 + Size1))) {
        return TRUE;
    }
    return FALSE;
}

static BOOLEAN AsciiStrOverlap(
    const CHAR8   *Str1,
    const UINTN   Size1,
    const CHAR8   *Str2,
    const UINTN   Size2)
{
    return IsBufOverlap(Str1, Size1, Str2, Size2);
}

static EFI_STATUS AsciiStrCheckParamter(
    CHAR8 *strDest,
    UINTN destMax,
    const CHAR8 *strSrc)
{
    BOOLEAN  bParamInvalid = FALSE;
    CHAR8 *pHeader = strDest;

    bParamInvalid = (destMax == 0 || destMax > EFI_SECUREC_ASCII_STRING_MAX_LEN);
    if (bParamInvalid) {
        return EFI_BAD_BUFFER_SIZE;
    }

    bParamInvalid = ((strDest == NULL) || (strSrc == NULL) || (strDest == strSrc));
    if (bParamInvalid) {
        if (strDest != NULL) {
            pHeader[0] = '\0';
        }
        return EFI_INVALID_PARAMETER;
    }

    return EFI_SUCCESS;
}

static EFI_STATUS AsciiStrnCheckParamter(
    CHAR8 *strDest,
    UINTN destMax,
    const CHAR8 *strSrc,
    UINTN count)
{
    EFI_STATUS eStatus;
    CHAR8 *pHeader = strDest;

    eStatus = AsciiStrCheckParamter(strDest, destMax, strSrc);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    // if dest address will overflow
    if (count >= destMax) {
        pHeader[0] = '\0'; // clear dest string
        return EFI_BUFFER_TOO_SMALL;
    }

    return EFI_SUCCESS;
}

/*******************************************************************************
* <FUNCTION DESCRIPTION>
*  Returns the length of a Null-terminated ascii string.
*  This function is similar as strnlen_s defined in C11.
*
* <INPUT PARAMETERS>
*  @param  cString   A pointer to a Null-terminated ascii string.
*  @param  iMaxSize  The maximum number of Destination ascii char, including terminating null char.
*
* <OUTPUT PARAMETERS>
*
* <RETURN VALUE>
*    0                     If String is NULL.
*    iMaxSize              There is no null character in the first MaxSize characters of String.
*    iMaxSize              The number of characters that percede the terminating null character.
*
********************************************************************************/
UINTN AsciiStrnLenS(
    CONST CHAR8 *cString,
    UINTN iMaxSize)
{
    UINTN iCount;

    // If String is a null pointer or MaxSize is 0, returns zero.
    if ((cString == NULL) || (iMaxSize == 0)) {
        return 0;
    }

    iCount = 0;
    while (cString[iCount] != '\0') { // no terminating null character
        if (iCount >= iMaxSize - 1) { // get to the first MaxSize characters of String
            return iMaxSize;
        }
        iCount++;
    }
    return iCount;
}

/*******************************************************************************
* <FUNCTION DESCRIPTION>
*    The AsciiStrCpyS function copies the contents in the address of strSrc,
*    including the terminating null character, to the location specified by strDest.
*    The destination string must be large enough to hold the source string,
*    including the terminating null character. AsciiStrCpyS will return EFI_ACCESS_DENIED
*    if the source and destination strings overlap.
*
* <INPUT PARAMETERS>
*    strDest                     Pointer to destination buffer.
*    destMax                     The max size(Character counts) of the destination buffer.
*    strSrc                      Pointer to source buffer.
*    count                       Number of characters to copy.
*
* <OUTPUT PARAMETERS>
*    dest buffer                is uptdated.
*
* <RETURN VALUE>
*    EFI_SUCCESS                        Success
*    EFI_INVALID_PARAMETER       strDest not terminated or NULL, strSrc is NULL, strDest==strSrc
*    EFI_BAD_BUFFER_SIZE           destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
*    EFI_ACCESS_DENIED             dest buffer and source buffer are overlapped
*    EFI_BUFFER_TOO_SMALL        source buffer length are large than destMax
*
********************************************************************************/
EFI_STATUS AsciiStrCpyS(
    CHAR8 *strDest,
    UINTN iDestMax,
    const CHAR8 *strSrc)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    UINTN        iStrSrcLen;
    CHAR8 *pDestPointer = strDest;
    const CHAR8 *pSrcPointer = strSrc;

    eStatus = AsciiStrCheckParamter(strDest, iDestMax, strSrc);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    iStrSrcLen = AsciiStrnLenS(strSrc, iDestMax); // source buffer length can be copy
    // strDest have not enough space, return error
    if (iStrSrcLen >= iDestMax) {
        pDestPointer[0] = '\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // if source and dest string is overlap
    bOverlap = AsciiStrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = '\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    while (*pSrcPointer != '\0') {
        *(pDestPointer++) = *(pSrcPointer++);
    }
    *pDestPointer = '\0';

    return EFI_SUCCESS;
}

/*******************************************************************************
* <FUNCTION DESCRIPTION>
*    Copy the contents from strSrc, including the terminating null character,
*    to the location specified by strDest.
*
* <INPUT PARAMETERS>
*    strDest                     Pointer to destination buffer.
*    destMax                     The max size(Character counts) of the destination buffer.
*    strSrc                      Pointer to source buffer.
*    count                       Number of characters to copy.
*
* <OUTPUT PARAMETERS>
*    dest buffer                is uptdated.
*
* <RETURN VALUE>
*    EFI_SUCCESS                        Success
*    EFI_INVALID_PARAMETER       strDest not terminated or NULL, strSrc is NULL, strDest==strSrc
*    EFI_BAD_BUFFER_SIZE           destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
*    EFI_BUFFER_TOO_SMALL        dest buffer overflow
*    EFI_ACCESS_DENIED             dest buffer and source buffer are overlapped
*
********************************************************************************/
EFI_STATUS AsciiStrnCpyS(
    CHAR8 *strDest,
    UINTN iDestMax,
    const CHAR8 *strSrc,
    UINTN count)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    UINTN        iStrSrcLen;
    CHAR8 *pDestPointer = strDest;
    const CHAR8 *pSrcPointer = strSrc;

    // check the parameter
    eStatus = AsciiStrnCheckParamter(strDest, iDestMax, strSrc, count);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    // no need copy
    if (count == 0) {
        pDestPointer[0] = '\0';
        return EFI_SUCCESS;
    }

    iStrSrcLen = AsciiStrnLenS(strSrc, MIN(iDestMax, count)); // source buffer length can be copy
    // strDest have not enough space, return error
    if (iStrSrcLen >= iDestMax) {
        pDestPointer[0] = '\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // StrnCheckParamter had confirm the count < iDestMax
    // so strnlen_s had confirm iStrSrcLen <= count

    // if source and dest string is overlap
    bOverlap = AsciiStrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = '\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    while ((iStrSrcLen > 0) && (*pSrcPointer != '\0')) {
        *(pDestPointer++) = *(pSrcPointer++);
        iStrSrcLen--;
    }
    *pDestPointer = '\0';

    return EFI_SUCCESS;
}

/*******************************************************************************
 * <FUNCTION DESCRIPTION>
 *    The AsciiStrCatS function appends strSrc to strDest and terminates the resulting
 *    string with a null character. The initial character of strSrc overwrites
 *    the terminating null character of strDest. AsciiStrCatS will return EFI_ACCESS_DENIED
 *    if the source and destination strings overlap.
 *
 *    Note that the second parameter is the total size of the buffer, not the remaining size.
 *
 * <INPUT PARAMETERS>
 *    strDest                Null-terminated destination string buffer.
 *    destMax                The max size(Character counts) of the destination buffer.
 *    strSrc                   Null-terminated source string buffer.
 *
 * <OUTPUT PARAMETERS>
 *    strDest                is updated
 *
 * <RETURN VALUE>
 *    EFI_SUCCESS                      Successful operation
 *    EFI_INVALID_PARAMETER     strDest not terminated or NULL, strSrc is NULL
 *    EFI_BAD_BUFFER_SIZE         destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
 *    EFI_BUFFER_TOO_SMALL      dest buffer overflow
 *    EFI_ACCESS_DENIED           dest buffer and source buffer are overlapped
 ********************************************************************************/
EFI_STATUS AsciiStrCatS(
    CHAR8 *strDest,
    UINTN iDestMax,
    const CHAR8 *strSrc)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    BOOLEAN    bBufferTooSmall = FALSE;
    UINTN        iStrDestLen;
    UINTN        iStrSrcLen;
    UINTN        iCopyLen;
    CHAR8 *pDestPointer = strDest;
    const CHAR8 *pSrcPointer = strSrc;

    eStatus = AsciiStrCheckParamter(strDest, iDestMax, strSrc);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    iStrDestLen = AsciiStrnLenS(strDest, iDestMax); // dest string length
    iCopyLen = iDestMax - iStrDestLen; // dest buffer length left for source string
    iStrSrcLen = AsciiStrnLenS(strSrc, iCopyLen); // source buffer length can be copy

    // strDest have not enough space, return error
    bBufferTooSmall = ((iCopyLen == 0) || (iCopyLen <= iStrSrcLen));
    if (bBufferTooSmall) {
        pDestPointer[0] = '\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // if source and dest string is overlap
    bOverlap = AsciiStrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = '\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    pDestPointer = strDest + iStrDestLen;
    while (*pSrcPointer != '\0') {
        *(pDestPointer++) = *(pSrcPointer++);
    }
    *pDestPointer = '\0';

    return EFI_SUCCESS;
}

/*******************************************************************************
 * <FUNCTION DESCRIPTION>
 *    The AsciiStrnCatS function try to append the first N characters of strSrc to
 *    the end of strDest, where N is the lesser of count and the length of strSrc.
 *
 * <INPUT PARAMETERS>
 *    strDest                  Destination string.
 *    destMax                  The max size of the destination buffer.
 *    strSrc                   Source string.
 *    count                    Number of character to append or truncate.
 *
 * <OUTPUT PARAMETERS>
 *    strDest
 *
 * <RETURN VALUE>
 *    EFI_SUCCESS              Successful operation
 *    EFI_INVALID_PARAMETER    strDest not terminated or NULL, strSrc is NULL
 *    EFI_BAD_BUFFER_SIZE      destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
 *    EFI_BUFFER_TOO_SMALL     dest buffer overflow
 *    EFI_ACCESS_DENIED        dest buffer and source buffer are overlapped
 *********************************************************************************/
EFI_STATUS AsciiStrnCatS(
    CHAR8 *strDest,
    UINTN iDestMax,
    const CHAR8 *strSrc,
    UINTN count)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    BOOLEAN    bBufferTooSmall = FALSE;
    UINTN        iStrDestLen;
    UINTN        iStrSrcLen;
    UINTN        iCopyLen;
    CHAR8 *pDestPointer = strDest;
    const CHAR8 *pSrcPointer = strSrc;

    eStatus = AsciiStrnCheckParamter(strDest, iDestMax, strSrc, count);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    // no need copy
    if (count == 0) {
        pDestPointer[0] = '\0';
        return EFI_SUCCESS;
    }

    // dest string length
    iStrDestLen = AsciiStrnLenS(strDest, iDestMax);
    // dest buffer length left for source string
    iCopyLen = iDestMax - iStrDestLen;
    // source buffer length can be copy
    iStrSrcLen = AsciiStrnLenS(strSrc, MIN(iCopyLen, count));
    // strDest have not enough space, return error
    bBufferTooSmall = ((iCopyLen == 0) || (iCopyLen <= iStrSrcLen));
    if (bBufferTooSmall) {
        pDestPointer[0] = '\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // had confirm iStrSrcLen <= count

    // if source and dest string is overlap
    bOverlap = AsciiStrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = '\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    pDestPointer = strDest + iStrDestLen;
    while ((iStrSrcLen > 0) && (*pSrcPointer != '\0')) {
        *(pDestPointer++) = *(pSrcPointer++);
        iStrSrcLen--;
    }
    *pDestPointer = '\0';

    return EFI_SUCCESS;
}

static BOOLEAN StrOverlap(
    const CHAR16   *Str1,
    const UINTN   Size1,
    const CHAR16   *Str2,
    const UINTN   Size2)
{
    return IsBufOverlap(Str1, Size1*sizeof(CHAR16), Str2, Size2*sizeof(CHAR16));
}

static EFI_STATUS StrCheckParamter(
    CHAR16 *strDest,
    UINTN destMax,
    const CHAR16 *strSrc)
{
    BOOLEAN  bParamInvalid = FALSE;
    CHAR16 *pHeader = strDest;

    bParamInvalid = (destMax == 0 || destMax > EFI_SECUREC_UNICODE_STRING_MAX_LEN);
    if (bParamInvalid) {
        return EFI_BAD_BUFFER_SIZE;
    }

    bParamInvalid = ((strDest == NULL) || (strSrc == NULL) || (strDest == strSrc));
    if (bParamInvalid) {
        if (strDest != NULL) {
            pHeader[0] = L'\0';
        }
        return EFI_INVALID_PARAMETER;
    }

    return EFI_SUCCESS;
}

static EFI_STATUS StrnCheckParamter(
    CHAR16 *strDest,
    UINTN destMax,
    const CHAR16 *strSrc,
    UINTN count)
{
    EFI_STATUS eStatus;
    CHAR16 *pHeader = strDest;

    eStatus = StrCheckParamter(strDest, destMax, strSrc);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    // if dest address will overflow
    if (count >= destMax) {
        pHeader[0] = L'\0'; // clear dest string
        return EFI_BUFFER_TOO_SMALL;
    }

    return EFI_SUCCESS;
}

/*******************************************************************************
* <FUNCTION DESCRIPTION>
*  Returns the length of a Null-terminated unicode string.
*  This function is similar as strnlen_s defined in C11.
*
* <INPUT PARAMETERS>
*  @param  cString   A pointer to a Null-terminated ascii string.
*  @param  iMaxSize  The maximum number of Destination ascii char, including terminating null char.
*
* <OUTPUT PARAMETERS>
*
* <RETURN VALUE>
*    0                     If String is NULL.
*    iMaxSize              There is no null character in the first MaxSize characters of String.
*    iMaxSize              The number of characters that percede the terminating null character.
*
********************************************************************************/
UINTN StrnLenS(
    CONST CHAR16 *cString,
    UINTN iMaxSize)
{
    UINTN iCount;

    // If String is a null pointer or MaxSize is 0, returns zero.
    if ((cString == NULL) || (iMaxSize == 0)) {
        return 0;
    }

    iCount = 0;
    while (cString[iCount] != L'\0') { // no terminating null character
        if (iCount >= iMaxSize - 1) { // get to the first MaxSize characters of String
            return iMaxSize;
        }
        iCount++;
    }
    return iCount;
}

/*******************************************************************************
* <FUNCTION DESCRIPTION>
*    The StrCpyS function copies the contents in the address of strSrc,
*    including the terminating null character, to the location specified by strDest.
*    The destination string must be large enough to hold the source string,
*    including the terminating null character. StrCpyS will return EFI_ACCESS_DENIED
*    if the source and destination strings overlap.
*
* <INPUT PARAMETERS>
*    strDest                     Pointer to destination buffer.
*    destMax                     The max size(Character counts) of the destination buffer.
*    strSrc                      Pointer to source buffer.
*
* <OUTPUT PARAMETERS>
*    dest buffer                is uptdated.
*
* <RETURN VALUE>
*    EFI_SUCCESS                        Success
*    EFI_INVALID_PARAMETER       strDest not terminated or NULL, strSrc is NULL, strDest==strSrc
*    EFI_BAD_BUFFER_SIZE           destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
*    EFI_ACCESS_DENIED             dest buffer and source buffer are overlapped
*    EFI_BUFFER_TOO_SMALL        source buffer length are large than destMax
*
********************************************************************************/
EFI_STATUS StrCpyS(
    CHAR16 *strDest,
    UINTN iDestMax,
    const CHAR16 *strSrc)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    UINTN      iStrSrcLen;
    CHAR16 *pDestPointer = strDest;
    const CHAR16 *pSrcPointer = strSrc;

    eStatus = StrCheckParamter(strDest, iDestMax, strSrc);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    iStrSrcLen = StrnLenS(strSrc, iDestMax); // source buffer length can be copy
    // strDest have not enough space, return error
    if (iStrSrcLen >= iDestMax) {
        pDestPointer[0] = L'\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // if source and dest string is overlap
    bOverlap = StrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = L'\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    while (*pSrcPointer != L'\0') {
        *(pDestPointer++) = *(pSrcPointer++);
    }
    *pDestPointer = L'\0';

    return EFI_SUCCESS;
}

/*******************************************************************************
* <FUNCTION DESCRIPTION>
*    Copy the contents from strSrc, including the terminating null character,
*    to the location specified by strDest.
*
* <INPUT PARAMETERS>
*    strDest                       Pointer to destination buffer.
*    destMax                       The max size(Character counts) of the destination buffer.
*    strSrc                        Pointer to source buffer.
*    count                         Number of characters to copy.
*
* <OUTPUT PARAMETERS>
*    dest buffer                is uptdated.
*
* <RETURN VALUE>
*    EFI_SUCCESS                 Success
*    EFI_INVALID_PARAMETER       strDest not terminated or NULL, strSrc is NULL, strDest==strSrc
*    EFI_BAD_BUFFER_SIZE         destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
*    EFI_BUFFER_TOO_SMALL        dest buffer overflow
*    EFI_ACCESS_DENIED           dest buffer and source buffer are overlapped
*
********************************************************************************/
EFI_STATUS StrnCpyS(
    CHAR16 *strDest,
    UINTN iDestMax,
    const CHAR16 *strSrc,
    UINTN count)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    UINTN        iStrSrcLen;
    CHAR16 *pDestPointer = strDest;
    const CHAR16 *pSrcPointer = strSrc;

    // check the parameter
    eStatus = StrnCheckParamter(strDest, iDestMax, strSrc, count);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    // no need copy
    if (count == 0) {
        pDestPointer[0] = L'\0';
        return EFI_SUCCESS;
    }

    iStrSrcLen = StrnLenS(strSrc, MIN(iDestMax, count)); // source buffer length can be copy
    // strDest have not enough space, return error
    if (iStrSrcLen >= iDestMax) {
        pDestPointer[0] = L'\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // StrnCheckParamter had confirm the count < iDestMax
    // so strnlen_s had confirm iStrSrcLen <= count

    // if source and dest string is overlap
    bOverlap = StrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = L'\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    while ((iStrSrcLen > 0) && (*pSrcPointer != L'\0')) {
        *(pDestPointer++) = *(pSrcPointer++);
        iStrSrcLen--;
    }
    *pDestPointer = L'\0';

    return EFI_SUCCESS;
}

/*******************************************************************************
 * <FUNCTION DESCRIPTION>
 *    The StrCatS function appends strSrc to strDest and terminates the resulting
 *    string with a null character. The initial character of strSrc overwrites
 *    the terminating null character of strDest. StrCatS will return EFI_ACCESS_DENIED
 *    if the source and destination strings overlap.
 *
 *    Note that the second parameter is the total size of the buffer, not the remaining size.
 *
 * <INPUT PARAMETERS>
 *    strDest                Null-terminated destination string buffer.
 *    destMax                The max size(Character counts) of the destination buffer.
 *    strSrc                 Null-terminated source string buffer.
 *
 * <OUTPUT PARAMETERS>
 *    strDest                is updated
 *
 * <RETURN VALUE>
 *    EFI_SUCCESS               Successful operation
 *    EFI_INVALID_PARAMETER     strDest not terminated or NULL, strSrc is NULL
 *    EFI_BAD_BUFFER_SIZE       destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
 *    EFI_BUFFER_TOO_SMALL      dest buffer overflow
 *    EFI_ACCESS_DENIED         dest buffer and source buffer are overlapped
 ********************************************************************************/
EFI_STATUS StrCatS(
    CHAR16 *strDest,
    UINTN iDestMax,
    const CHAR16 *strSrc)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    BOOLEAN    bBufferTooSmall = FALSE;
    UINTN        iStrDestLen;
    UINTN        iStrSrcLen;
    UINTN        iCopyLen;
    CHAR16 *pDestPointer = strDest;
    const CHAR16 *pSrcPointer = strSrc;

    eStatus = StrCheckParamter(strDest, iDestMax, strSrc);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    iStrDestLen = StrnLenS(strDest, iDestMax); // dest string length
    iCopyLen = iDestMax - iStrDestLen; // dest buffer length left for source string
    iStrSrcLen = StrnLenS(strSrc, iCopyLen); // source buffer length can be copy

    // strDest have not enough space, return error
    bBufferTooSmall = ((iCopyLen == 0) || (iCopyLen <= iStrSrcLen));
    if (bBufferTooSmall) {
        pDestPointer[0] = L'\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // if source and dest string is overlap
    bOverlap = StrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = L'\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    pDestPointer = strDest + iStrDestLen;
    while (*pSrcPointer != L'\0') {
        *(pDestPointer++) = *(pSrcPointer++);
    }
    *pDestPointer = L'\0';

    return EFI_SUCCESS;
}

/*******************************************************************************
 * <FUNCTION DESCRIPTION>
 *    The StrnCatS function try to append the first N characters of strSrc to
 *    the end of strDest, where N is the lesser of count and the length of strSrc.
 *    If appending those N characters will fit within strDest (whose size is given
 *    as destMax) and still leave room for a null terminator, then those characters
 *    are appended, starting at the original terminating null of strDest, and a
 *    new terminating null is appended; otherwise, strDest[0] is set to the null
 *    character.
 *
 * <INPUT PARAMETERS>
 *    strDest                  Null-terminated destination string.
 *    destMax                  The max size(Character counts) of the destination buffer.
 *    strSrc                   Null-terminated source string.
 *    count                    Number of characters to append, or truncate.
 *
 * <OUTPUT PARAMETERS>
 *    strDest                  is updated
 *
 * <RETURN VALUE>
 *    EFI_SUCCESS               Successful operation
 *    EFI_INVALID_PARAMETER     strDest not terminated or NULL, strSrc is NULL
 *    EFI_BAD_BUFFER_SIZE       destMax is 0, too small, or destMax > SECUREC_STRING_MAX_LEN
 *    EFI_BUFFER_TOO_SMALL      dest buffer overflow
 *    EFI_ACCESS_DENIED         dest buffer and source buffer are overlapped
 *********************************************************************************/
EFI_STATUS StrnCatS(
    CHAR16 *strDest,
    UINTN iDestMax,
    const CHAR16 *strSrc,
    UINTN count)
{
    EFI_STATUS eStatus;
    BOOLEAN    bOverlap = FALSE;
    BOOLEAN    bBufferTooSmall = FALSE;
    UINTN        iStrDestLen;
    UINTN        iStrSrcLen;
    UINTN        iCopyLen;
    CHAR16 *pDestPointer = strDest;
    const CHAR16 *pSrcPointer = strSrc;

    eStatus = StrnCheckParamter(strDest, iDestMax, strSrc, count);
    if (EFI_ERROR(eStatus)) {
        return eStatus;
    }

    // no need copy
    if (count == 0) {
        pDestPointer[0] = L'\0';
        return EFI_SUCCESS;
    }

    // dest string length
    iStrDestLen = StrnLenS(strDest, iDestMax);
    // dest buffer length left for source string
    iCopyLen = iDestMax - iStrDestLen;
    // source buffer length can be copy
    iStrSrcLen = StrnLenS(strSrc, MIN(iCopyLen, count));
    // strDest have not enough space, return error
    bBufferTooSmall = ((iCopyLen == 0) || (iCopyLen <= iStrSrcLen));
    if (bBufferTooSmall) {
        pDestPointer[0] = L'\0';
        return EFI_BUFFER_TOO_SMALL;
    }

    // had confirm iStrSrcLen <= count

    // if source and dest string is overlap
    bOverlap = StrOverlap(strDest, iDestMax, strSrc, iStrSrcLen + 1);
    if (bOverlap) {
        pDestPointer[0] = L'\0';
        return EFI_ACCESS_DENIED;
    }

    // copy
    pDestPointer = strDest + iStrDestLen;
    while ((iStrSrcLen > 0) && (*pSrcPointer != L'\0')) {
        *(pDestPointer++) = *(pSrcPointer++);
        iStrSrcLen--;
    }
    *pDestPointer = L'\0';

    return EFI_SUCCESS;
}

